/*
esp8266.c
offer exp8266 wifi module driver
designed by lunanting

----------------------
2020-08-27 22:04:44
create file
----------------------
*/


#include "esp8266.h"
#include "../../../../include/aos/hal/uart.h"
#include "../../../bsp/cli_cmd.h"
#include "stdio.h"
#include "../../../bsp/xp_uart.h"
#include "stdlib.h"
#include "string.h"
#include "k_api.h"


static ESP8266_class esp8266={0};
static uart_dev_t uart;
static aos_sem_t sem_s,sem_r,sem_msg;
static aos_mutex_t response_mutex;
static ESP8266_buffer rec_buffer[5]={0};
static ESP8266_ap wifi_ap={0};


static char esp8266_cmd[][20]={
    "",
    "RST",
    "ATE0",
    "ATE1",
    "RESTORE",
    "CWMODE",
    "CWJAP_DEF",
    "CWQAP",
    "CIFSR",
    "CIPSTART",
    "CIPSEND",
    "CIPMUX",
    "CIPSTA_DEF",
    "CIPSERVER",
    "CIPCLOSE",
    "CIPSERVERMAXCONN",
    "CIPMODE",
};

void __weak xp_esp8266_rec_api(u8 link,u32 len,char *data){
    println("esp8266 recive:link=%d,len=%d,data=%s",link,len,data);
}



/*
esp8266 init 
param:
    port:uart port
    speed:uart speed
return:
    0=success
*/
int xp_esp8266_init(int port,int speed){

    if(esp8266.init==1)return 0;
    esp8266.init=0;
    uart.port=esp8266.port=port;
    uart.config.baud_rate=esp8266.speed=speed;
    uart.config.data_width=DATA_WIDTH_8BIT;
    uart.config.parity=NO_PARITY;
    uart.config.stop_bits=STOP_BITS_1;
    uart.config.flow_control=FLOW_CONTROL_DISABLED;
    uart.config.mode=MODE_TX_RX;

    esp8266.response=0;
    if(hal_uart_init(&uart)!=0){
        println("esp8266 uart init fail~");
        return -1;
    }

    void xp_esp8266_recive_thread(void *arg);
    if(aos_task_new("esp8266 recive",xp_esp8266_recive_thread,NULL,3000)!=0){
        println("esp826 recive thread create fail ~~");
        return -2;
    }
    if(aos_sem_new(&sem_s,1)!=0){
        println("esp826 send sem create fail ~~");
        return -3;
    }
    if(aos_sem_new(&sem_r,0)!=0){
        println("esp826 recive sem create fail ~~");
        return -4;
    }
    if(aos_mutex_new(&response_mutex)!=0){
        println("esp826 response mutex create fail ~~");
        return -5;
    }
    if(aos_sem_new(&sem_msg,0)!=0){
        println("esp826 recive msg sem create fail ~~");
        return -6;
    }
    void xp_esp8266_thread(void *arg);
    if(aos_task_new("esp8266",xp_esp8266_thread,NULL,3000)!=0){
        println("esp826 thread create fail ~~");
        return -7;
    }
    println("esp8266 module init success!");
    esp8266.init=1;
    return 0;
}


/*
esp8266 recive data thread 
*/
void xp_esp8266_recive_thread(void *arg){
    char buf[100];
    int rec_len=0;
    char *foot,*p,*f1;
    char split[5]="\n";
    static u8 bf=0;

    println("esp8266 recive data thread create success !");
    while(true){
        xp_uart_recive(&uart,(u8 *)buf,99,&rec_len,5);
        println("%s",buf);
        p=strtok_r(buf,split,&foot);
        while(1){
            if(p==NULL)break;
            if(strstr(p,"OK")!=NULL){
                xp_esp8266_set_response(1);
                aos_sem_signal(&sem_r);
            }
            else if(strstr(p,"ERROR")!=NULL){
                xp_esp8266_set_response(2);
                aos_sem_signal(&sem_r);
            }
            else if(strstr(p,">")!=NULL){
                esp8266.writeFlage=1;
            }
            else if(strstr(p,"+IPD")==p){
                //处理接收的数据
                p=strtok_r(p,",",&f1);
                if(f1!=NULL){
                    if(esp8266.mux==0){     //单链接模式
                        rec_buffer[bf].link=0;
                    }
                    else{   //多连接模式
                        p=strtok_r(NULL,",",&f1);
                        if(p!=NULL){
                            u8 i=atoi(p);
                            if(i<5){
                                rec_buffer[bf].link=i;
                            }
                            else{
                                f1=NULL;
                            }
                        }
                    }
                    if(f1!=NULL){
                        p=strtok_r(NULL,":",&f1);
                        if(p!=NULL&&f1!=NULL){
                            rec_buffer[bf].len=atoi(p);
                            if(rec_buffer[bf].len>100){
                                rec_buffer[bf].len=100;
                            }
                            memcpy(rec_buffer[bf].data,f1,rec_buffer[bf].len);
                            bf++;
                            if(bf>4)bf=0;
                            aos_sem_signal(&sem_msg);
                        }
                    }
                }
            }
            else if(strstr(p,"+CIFSR")==p){
                //接收到 fsr 的ip 信息

            }
            else if(strstr(p,"SEND OK")==p){
                esp8266.sendFlage=1;
            }
            else if(strstr(p,"WIFI CONNECTED")==p){
                esp8266.connect_wifi=1;
            }
            else if(strstr(p,"WIFI DISCONNECT")==p){
                esp8266.connect_wifi=0;
            }

            p=strtok_r(NULL,split,&foot);
        }
        memset(buf,0,rec_len);
    }
}


/*
esp8266 thread
负责模块的初始化，消息的处理
*/
void xp_esp8266_thread(void *arg){
    static u8 bf=0;
    ESP8266_buffer msg;
    
    println("esp8266 thread create success!");
    aos_msleep(2000);
    xp_esp8266_check_ready();
    xp_esp8266_config(1);
    while(1){
        aos_sem_wait(&sem_msg,AOS_WAIT_FOREVER);
        memcpy(&msg,&rec_buffer[bf],sizeof(ESP8266_buffer));
        memset(&rec_buffer[bf],0,rec_buffer[bf].len);
        bf++;
        if(bf>4)bf=0;
        xp_esp8266_rec_api(msg.link,msg.len,msg.data);
    }
}




/*
esp8266 send data
param:
    cmd:cmd foot number ESP8266_cmd type
    param:param string,NULL=no param
return:
    0=success
*/
int xp_esp8266_cmd(const ESP8266_cmd cmd,const char *param){
    char buf[100];
    int sta=0;
    int rec;
    int count=0;

    while(esp8266.writeFlage==1){
        count++;
        if(count>30){
            println("esp8266 write flage overtime~");
            return -4;
        }
        aos_msleep(50);
    }
    if(cmd==esp8266_test){
        sprintf(buf,"AT\r\n");
    }
    else if(cmd==esp8266_ate0||cmd==esp8266_ate1){
        sprintf(buf,"%s\r\n",esp8266_cmd[cmd]);
    }
    else if(param==NULL){
        sprintf(buf,"AT+%s\r\n",esp8266_cmd[cmd]);
    }
    else{
        sprintf(buf,"AT+%s=%s\r\n",esp8266_cmd[cmd],param);
    }
    if(aos_sem_wait(&sem_s,16000)!=0){       //等等上次发送超时退出
        return -1;
    }
    println("%s",buf);
    xp_esp8266_set_response(0);
    krhino_sem_count_set(sem_r.hdl,0);
    if(hal_uart_send(&uart,buf,strlen(buf),10)!=0){
        sta=-2;
        goto END;
    }
    if(aos_sem_wait(&sem_r,15000)!=0){
        sta=-3;         //无应答
        goto END;
    }
    rec=xp_esp8266_get_response();
    if(rec==1){
        sta=0;      //返回ok 等等
    }
    else if(rec==2){
        sta=1;      //返回error 等等
    }

END:aos_sem_signal(&sem_s);
    return sta;
}


/*
get response status
return:status
    0=nothing
    1=ok ...
    2=error ...
*/
int xp_esp8266_get_response(void){
    int sta;
    aos_mutex_lock(&response_mutex,AOS_WAIT_FOREVER);
    sta=esp8266.response;
    if(sta!=0){
        esp8266.response=0;
    }
    aos_mutex_unlock(&response_mutex);
    return sta;
}

/*
set response status
param:
    status:0=nothing,1=ok...,2=error...
*/
void xp_esp8266_set_response(int status){
    aos_mutex_lock(&response_mutex,AOS_WAIT_FOREVER);
    esp8266.response=status;
    aos_mutex_unlock(&response_mutex);
}




/*
check module ready
return:
    0=ready
*/
int xp_esp8266_check_ready(void){
    xp_esp8266_cmd(esp8266_rst,NULL);
    aos_msleep(1000);
    while(1){
        if(xp_esp8266_cmd(esp8266_test,NULL)==0){
            println("esp8266 module ready!");
            esp8266.ready=1;
            return 0;
        }
        aos_msleep(1000);
    }
    return -1;
}


/*
send data 
param:
    data:send data
    len:send data lenght
return:
    0=success
*/
int xp_esp8266_send(char *data,int len){
    char buf[10];
    int count=0;
    int sta=0;
    
    if(esp8266.connect_wifi!=1){
        println("not connected wifi!");
        return -1;
    }
    sprintf(buf,"%d",len);
    if(xp_esp8266_cmd(esp8266_cipsend,buf)!=0){
        println("esp8266 send data fail~");
        return -1;
    }
    if(esp8266.writeFlage!=1)return -4;
    esp8266.sendFlage=0;
    if(hal_uart_send(&uart,data,len,10)!=0){
        sta=-2;
        goto END;
    }

    while(esp8266.sendFlage!=1){
        count++;
        if(count>20){
            sta=-3;
            goto END;
        }
        aos_msleep(50);
    }
END:esp8266.writeFlage=0;
    return sta;
}






/*
connect to wifi ap
param:
    wifi_name:wifi name
    wifi_password:wifi password
return:
    0=success
*/
int xp_esp8266_connect_ap(char *wifi_name,char *wifi_password){
    char buf[50];
    u8 count=200;

    if(esp8266.ready!=1){
        println("esp8266 not ready!");
        return -1;
    }
    if(xp_esp8266_cmd(esp8266_cwmode,"3")!=0)return -1;
    sprintf(buf,"\"%s\",\"%s\"",wifi_name,wifi_password);
    xp_esp8266_cmd(esp8266_cwjap,buf);      //短时间没有返回连接状态
    while(count--){
        if(esp8266.connect_wifi==1){
            break;
        }
        aos_msleep(100);
    }
    if(count==0){   //超时仍未连接上
        return -1;
    }
    return 0;
}


/*
module base config
param:
    mode:传输模式，0=普通模式，1=透传模式
    mux:连接模式，0=单连，只用一个连接，1=多连，最多同时可以用5个连接，同时需要mode=0
return:
    0=success
*/
int xp_esp8266_config(u8 mux){
    char buf[5];
    
    xp_esp8266_cmd(esp8266_ate0,NULL);
    if(xp_esp8266_cmd(esp8266_cipmux,"0")!=0)return -2;
    if(xp_esp8266_cmd(esp8266_cipmode,"0")!=0)return -1;
    sprintf(buf,"%d",mux);
    if(xp_esp8266_cmd(esp8266_cipmux,buf)!=0)return -2;
    if(mux==1){
        esp8266.mux=1;
    }
    else{
        esp8266.mux=0;
    }
    if(xp_esp8266_wifi_config("ygl-06","yigongli06")!=0){
        aos_task_exit(0);
    }

    return 0;
}



/*
create a tcp client and connect to the server
param:
    link:link id
    ip:server ip string
    port:server port
return:
    0=success
*/
int xp_esp8266_create_client(u8 link,char *ip,int port){
    char buf[50];

    if(esp8266.connect_wifi!=1)return -2;
    xp_esp8266_link_close(link);
    if(esp8266.mux==1){
        sprintf(buf,"%d,\"TCP\",\"%s\",%d",link,ip,port);
    }
    else{
        sprintf(buf,"\"TCP\",\"%s\",%d",ip,port);
    }
    
    if(strlen(ip)>16)return -3;
    if(xp_esp8266_cmd(esp8266_cipstart,buf)!=0)return -1;
    return 0;
}


/*
close link
param:
    link:link id 0~4,5=all close,开启server后5无效
return:
    0=success
*/
int xp_esp8266_link_close(u8 link){
    char buf[5];
    sprintf(buf,"%d",link);
    if(esp8266.mux==1){
        if(xp_esp8266_cmd(esp8266_cipclose,buf)!=0){
            return -1;
        }
    }
    else{
        if(xp_esp8266_cmd(esp8266_cipclose,NULL)!=0){
            return -1;
        }
    }
    return 0;
}


/*
create a server and set server open state
创建一个服务，并设定服务开启状态,最多允许3个client连接
param:
    port: server port
    open:open status ,0=close,1=open
    obj:server object
return:
    0=success
*/
int xp_esp8266_create_server(u8 open,int port,ESP8266_server *obj){
    char buf[50];

    if(esp8266.connect_wifi!=1)return -1;
    sprintf(buf,"%d,%d",open,port);
    obj->port=port;
    obj->open=open;
    obj->esp8266=&esp8266;
    // if(xp_esp8266_cmd(esp8266_cipservermaxconn,"4")!=0){
    //     println("esp8266 create server set fail,set max client fail~");
    //     return -2;
    // }
    if(xp_esp8266_cmd(esp8266_cipserver,buf)!=0){
        println("esp8266 create server fail~");
        return -1;
    }
    return 0;
}


/*
set server open status
param:
    obj:server object
    sta:to set status
return:
    0=success
*/
int xp_esp8266_server_set(ESP8266_server *obj,bool sta){
    char buf[5];

    sprintf(buf,"%d",sta);
    if(xp_esp8266_cmd(esp8266_cipserver,buf)!=0){
        println("esp8266 set server open status fail~");
        return -1;
    }
    obj->open=sta;
    return 0;
}


/*
set sta mode static ip
param:
    ip: set ip string
return:
    0=success
*/
int xp_esp8266_set_staip(char *ip){
    char buf[30];
    sprintf(buf,"\"%s\"",ip);
    if(xp_esp8266_cmd(esp8266_cipsta,buf)!=0){
        println("esp8266 set sta ip fail~");
        return -1;
    }
    return 0;
}


/*
config wifi ap message
param:
    name:wifi name
    password:wifi password
return:
    0=success
*/
int xp_esp8266_wifi_config(char *name,char *password){
    if(name==NULL||password==NULL)return -1;
    if(strlen(name)>20||strlen(password)>20)return -2;

    strcpy(wifi_ap.name,name);
    strcpy(wifi_ap.password,password);
    xp_esp8266_cmd(esp8266_cwqap,NULL);
    aos_msleep(2000);
    if(xp_esp8266_connect_ap(wifi_ap.name,wifi_ap.password)!=0)return -3;
    return 0;
}


/*
new a tcp client
param:
    link:link id
    server_ip:server ip
    server_port:server port
return:
    NULL=new fail
    client object
*/
ESP8266_client *xp_esp8266_client_new(u8 link,char *server_ip,int server_port){
    ESP8266_client *client=(ESP8266_client*)malloc(sizeof(ESP8266_client));
    int sta=0;

    memset(client,0,sizeof(ESP8266_client));
    if(link>4){
        println("link id set error,link=%d",link);
        sta=-1;
        goto END;
    }
    if(strlen(server_ip)>16){
        println("server ip set error~");
        sta=-2;
        goto END;
    }
    if(esp8266.init!=1){
        println("esp8266 uninit~");
        sta=-3;
        goto END;
    }
    client->link_id=link;
    strcpy(client->target_ip,server_ip);
    client->esp8266=&esp8266;
    client->target_port=server_port;
    if(xp_esp8266_create_client(link,server_ip,server_port)!=0){
        println("create client fail~");
        sta=-4;
        goto END;
    }

END:if(sta!=0){
        free(client);
        client=NULL;
    }   
    return client;
}


/*
new a server
param:
    open:open status,0=close,1=open
    port:server port
return:ESP8266_server object
    NULL=fail
    object=suceess
*/
ESP8266_server *xp_esp8266_server_new(bool open,int port){
    ESP8266_server *server=(ESP8266_server *)malloc(sizeof(ESP8266_server));
    int sta=0;

    memset(server,0,sizeof(ESP8266_server));
    if(esp8266.init!=1){
        println("esp8266 uninit~");
        sta=-1;
        goto END;
    }
    if(xp_esp8266_create_server(1,6666,server)!=0){
        println("create server fail~");
        sta=-2;
        goto END;
    }

END:
    if(sta!=0){
        free(server);
        server=NULL;
    }
    return server;
}



/**********************************test*********************************/
static ESP8266_client *test_client;
static ESP8266_server *test_server;

//esp8266 test
void xp_esp8266_client_test(void *arg){
  

    if(xp_esp8266_init(5,115200)!=0){
        aos_task_exit(0);
    }
    while(esp8266.ready!=1){aos_msleep(100);}
    while(esp8266.connect_wifi!=1){
        aos_msleep(100);
    }

    test_client=xp_esp8266_client_new(0,"192.168.6.111",6666);
    if(test_client==NULL){
        println("create client fail ~");
        aos_task_exit(0);
    }
    while(1){
        aos_msleep(1000);
    }
}


//esp8266 server test
void xp_esp8266_server_test(void *arg){
    

    if(xp_esp8266_init(5,115200)!=0){
        aos_task_exit(0);
    }
    while(esp8266.ready!=1){aos_msleep(100);}
    while(esp8266.connect_wifi!=1){
        aos_msleep(100);
    }
    test_server=xp_esp8266_server_new(1,6666);
    if(test_server==NULL){
        println("new a server fail~");
        aos_task_exit(0);
    }
    while(1){
        aos_msleep(500);
    }
}

/*********************************debug*********************************/
//esp8266 debug
int xp_esp8266_debug(char *type,char *fun,char *param){
    char buf[50];

    if(strcmp(type,"esp8266")!=0)return 0;
    else if(strcmp(fun,"client")==0){
        if(strcmp(param,"create")==0){
            aos_task_new("esp8266 client 0",xp_esp8266_client_test,NULL,3000);
        }
        else if(strcmp(param,"repare")==0){
            xp_esp8266_create_client(test_client->link_id,test_client->target_ip,test_client->target_port);
        }
    }
    else if(strcmp(fun,"server")==0){
        if(strcmp(param,"create")==0){
            aos_task_new("esp8266 server",xp_esp8266_server_test,NULL,3000);
        }
        else if(strcmp(param,"repare")==0){
            xp_esp8266_server_set(test_server,0);
            aos_msleep(100);
            xp_esp8266_server_set(test_server,1);
        }

    }
    else{
        if(strcmp(param,"NULL")==0){
            sprintf(buf,"AT+%s\r\n",fun);
        }
        else{
            sprintf(buf,"AT+%s=%s\r\n",fun,param);
        }
        hal_uart_send(&uart,buf,strlen(buf),10);
    }


    return 1;
}



//end of the file
